package priv.conceit.sc.hoxton.common.web.security;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import priv.conceit.sc.hoxton.common.cache.util.RedisExecuteUtil;
import priv.conceit.sc.hoxton.common.constant.auth.user.UserLoginDeviceTypeEnum;
import priv.conceit.sc.hoxton.common.constant.cache.CachePrefixKeyEnum;
import priv.conceit.sc.hoxton.common.entity.auth.authority.dto.CurrentUserInfoDTO;
import priv.conceit.sc.hoxton.common.entity.auth.authority.pojo.Resource;
import priv.conceit.sc.hoxton.common.entity.auth.authority.pojo.Role;
import priv.conceit.sc.hoxton.common.entity.auth.authority.pojo.RoleResource;
import priv.conceit.sc.hoxton.common.entity.auth.authority.pojo.UserRole;
import priv.conceit.sc.hoxton.common.web.exception.WebException;
import priv.conceit.sc.hoxton.common.web.exception.WebExceptionEnum;

import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * token处理工具类
 * 1-生成缓存key
 * 2-提供key与token的转换
 * 3-生产token
 * 4-token的解析
 * 5-其他功能
 *
 * token(对外名称 加密过的) 外部传入token
 *
 * cacheToken（未加密的token）  = token
 * WholeCacheToken token在缓存中的全名称 示例xx:xx:token
 *
 * @author conceit
 * @since 1.0.0, 2019/9/20
 */
@Component
@Slf4j
public class TokenUtil {


	@Autowired
	private RedisTemplate redisTemplate;

	@Autowired
	private RedisExecuteUtil redisExecuteUtil;


	/**
	 * 加密字符串
	 * 使用Blowfish加密算法
	 * 加密模式 ECB
	 * 填充 pkcs5
	 * 字符集 UTF8
	 * 输出格式 hex
	 * @param unEncryptedStr
	 * @param secretKey
	 * @return java.lang.String
	 * @author conceit
	 * @date 2020/11/13 10:18
	 */
	private String encryptedStr(String unEncryptedStr,String secretKey){
		SymmetricCrypto blowfish = null;
		try {
			blowfish = new SymmetricCrypto(SymmetricAlgorithm.Blowfish,secretKey.getBytes("UTF-8"));
		} catch (UnsupportedEncodingException e) {
			log.error("加密token出现异常！",e);
			throw new WebException(WebExceptionEnum.EX_USER_TOKEN_GENERATE_ERROR);
		}
		String encryptHex = blowfish.encryptHex(unEncryptedStr);
		return encryptHex;
	}

	/**
	 * 解密字符串
	 * 使用Blowfish加密算法
	 * 加密模式 ECB
	 * 填充 pkcs5
	 * 字符集 UTF8
	 * 输出格式 hex
	 * @param encryptedStr 已加密的字符串
	 * @param secretKey 秘钥
	 * @return java.lang.String
	 * @author conceit
	 * @date 2020/11/13 10:18
	 */
	private String decryptStr(String encryptedStr,String secretKey){
		SymmetricCrypto blowfish=null;
		//解密token
		try {
			blowfish=new SymmetricCrypto(SymmetricAlgorithm.Blowfish.getValue(),secretKey.getBytes("UTF-8"));
		} catch (UnsupportedEncodingException e) {
			log.error("加密token出现异常！",e);
			throw new WebException(WebExceptionEnum.EX_USER_TOKEN_GENERATE_ERROR);
		}
		String unEncryptedStr = blowfish.decryptStr(encryptedStr, CharsetUtil.CHARSET_UTF_8);
		return unEncryptedStr;
	}


	/**
	 * generateCacheToken 生成token（加密过的）
	 *
	 * @param userId
     * @param userLoginDeviceTypeEnum
	 * @param separator
	 * @param secretKey
	 * @return java.lang.String
	 * @author conceit
	 * @date 2020/11/13 13:54
	 */
	public String generateCacheToken(Long userId,
								UserLoginDeviceTypeEnum userLoginDeviceTypeEnum,
								String separator,String secretKey){

		//生成可读的token
		//String unEncryptedToken=this.generateUnEncryptedCacheToken(userId,userLoginDeviceTypeEnum,separator);
		//加密token
		//String encryptedToken=this.generateEncryptedCacheToken(unEncryptedToken,secretKey);
		//加上后置
		return null;
	}

	/**
	 * generateCacheToken 生成保存的cacheKey,由前置的key加上已经加密的token传递而来
	 *
	 * @param encryptedToken
	 * @return java.lang.String
	 * @author conceit
	 * @date 2020/11/13 10:31
	 */
	public String generateWholeCacheToken(String encryptedToken){
		return redisExecuteUtil.buildProjectKey(CachePrefixKeyEnum.AUTH_TOKEN,encryptedToken);
	}


	/**
	 * parseCacheToken 解析为可读已经拆分的k-v
	 *
	 * @param token
	 * @param separator
	 * @param secretKey
	 * @return Map
	 * @author conceit
	 * @date 2020/11/9 18:50
	 */
	public Map<String,String> parseCacheToken(String token,String separator,String secretKey){


		String unEncryptedToken = decryptStr(token,secretKey);

		//分割key
		String[] keyArray= unEncryptedToken.split(separator);
		Map<String,String> map=new HashMap<>();
		map.put("userId",keyArray[0]);
		map.put("userLoginDeviceTypeEnum",keyArray[1]);
		map.put("userLoginDeviceTypeEnum",keyArray[1]);
		return map;
	}


	/**
	 * saveToken 保存token至db或redis(有则更新)
	 *
	 * @param token

	 * @param expiresTime
	 * @param expiresTimeUnit
	 * @return java.lang.Boolean
	 * @author conceit
	 * @date 2020/11/9 18:47
	 */
	public Boolean saveOrUpdateToken(String token,long expiresTime, TimeUnit expiresTimeUnit) {

		String key=this.generateWholeCacheToken(token);

		Boolean flag=false;
		//查询key是否存在
		if(redisTemplate.hasKey(key)){
			//更新
			flag=redisTemplate.opsForValue().setIfPresent(key,null,expiresTime,expiresTimeUnit);
			return flag;
		}
		//新增
		flag=redisTemplate.opsForValue().setIfAbsent(key,null,expiresTime,expiresTimeUnit);
		return flag;
	}

	/**
	 * delToken 删除token
	 *
	 * @param token
	 * @author conceit
	 * @date 2020/11/9 18:47
	 */
	public  Boolean del(String token){
		String key=this.generateWholeCacheToken(token);
		return redisTemplate.delete(key);
	}


	/**
	  * users2UserDetails
	  *
	  * @param currentUserInfoDTO
	  * @return: org.springframework.security.core.userdetails.UserDetails
	  * @author: conceit
	  * @date: 2019/9/21 14:20
	  */
	public UserDetails userInfo2UserDetails(CurrentUserInfoDTO currentUserInfoDTO) {
		WebUserDetail webUserDetail = new WebUserDetail();
		webUserDetail.setAuthorities(this.mapGrantedAuthorities(this.mapResourcesValue(currentUserInfoDTO.getAuthority().getResources())));
		webUserDetail.setCurrentUserInfoDTO(currentUserInfoDTO);
		return webUserDetail;
	}

	/**
	 * mapResourcesValue
	 *
	 * @param resources
	 * @return: java.util.List<java.lang.String>
	 * @author: conceit
	 * @date: 2019/4/25 17:27
	 */
	public List<String> mapResourcesValue(List<Resource> resources) {
		return resources.stream().filter(x-> Objects.nonNull(x)).map(x -> x.getValue()).collect(Collectors.toList());
	}

	/**
	 * mapRoleValue
	 *
	 * @param roles
	 * @return: java.util.List<java.lang.String>
	 * @author: conceit
	 * @date: 2019/5/22 14:46
	 */
	public List<String> mapRoleValue(List<Role> roles){
		return roles.stream().filter(x-> Objects.nonNull(x)).map(x->x.getValue()).collect(Collectors.toList());
	}

	/**
	 * mapGrantedAuthorities
	 *
	 * @param resourcesKeys
	 * @return: java.util.List<org.springframework.security.core.GrantedAuthority>
	 * @author: conceit
	 * @date: 2019/4/25 17:29
	 */
	public List<GrantedAuthority> mapGrantedAuthorities(List<String> resourcesKeys) {
		return resourcesKeys.stream().filter(x-> StrUtil.isNotBlank(x))
				.map(SimpleGrantedAuthority::new)
				.collect(Collectors.toList());
	}



	/**
	 * matchRoleIds 根据用户id匹配出其对应的角色id集合
	 *
	 * @param userRoles
     * @param userId
	 * @return java.util.List<priv.conceit.sc.hoxton.common.entity.auth.authority.pojo.Role>
	 * @author conceit
	 * @date 2020/12/10 10:14
	 */
	public List<Long> matchRoleIds(List<UserRole> userRoles,Long userId){
		if(CollUtil.isEmpty(userRoles)||Objects.isNull(userId)){
			return new ArrayList<>();
		}
		return userRoles
				.stream()
				.filter(item->item.getUserId().equals(userId))
				.map(UserRole::getRoleId)
				.collect(Collectors.toList());
	}

	/**
	 * matchRoles 匹配出对应的角色集合
	 *
	 * @param roles
     * @param roleIds
	 * @return java.util.List<priv.conceit.sc.hoxton.common.entity.auth.authority.pojo.Role>
	 * @author conceit
	 * @date 2020/12/10 10:25
	 */
	public List<Role> matchRoles(List<Role> roles,List<Long> roleIds){
		List<Role> matchedRoles=new ArrayList<>();
		if(CollUtil.isEmpty(roles)||CollUtil.isEmpty(roleIds)){
			return matchedRoles;
		}

		for (Role role:roles){
			for(Long roleId:roleIds){
				if(roleId.equals(role.getId())){
					matchedRoles.add(role);
				}
			}
		}
		return matchedRoles;
	}

	/**
	 * matchResourceIds 匹配resourceIds
	 *
	 * @param roleResources
     * @param roleIds
	 * @return java.util.List<java.lang.Long>
	 * @author conceit
	 * @date 2020/12/10 11:34
	 */
	public List<Long> matchResourceIds(List<RoleResource> roleResources, List<Long> roleIds){
		List<Long> matchedResourceIds=new ArrayList<>();
		if(CollUtil.isEmpty(roleResources)||CollUtil.isEmpty(roleIds)){
			return matchedResourceIds;
		}

		for (RoleResource roleResource:roleResources){
			for(Long roleId:roleIds){
				if(roleId.equals(roleResource.getRoleId())){
					matchedResourceIds.add(roleResource.getResourceId());
				}
			}
		}
		return matchedResourceIds;
	}

	/**
	 * matchResources 匹配出对应的资源集合
	 *
	 * @param resources
     * @param resourceIds
	 * @return java.util.List<priv.conceit.sc.hoxton.common.entity.auth.authority.pojo.Resource>
	 * @author conceit
	 * @date 2020/12/10 14:19
	 */
	public List<Resource> matchResources(List<Resource> resources,List<Long> resourceIds){
		List<Resource> matchedResources=new ArrayList<>();
		if(CollUtil.isEmpty(resources)||CollUtil.isEmpty(resourceIds)){
			return matchedResources;
		}

		for (Resource resource:resources){
			for(Long resourceId:resourceIds){
				if(resourceId.equals(resource.getId())){
					matchedResources.add(resource);
				}
			}
		}

		return matchedResources;
	}
}
